home *** CD-ROM | disk | FTP | other *** search
/ Aminet 5 / Aminet 5 - March 1995.iso / Aminet / util / moni / PriMan20.lha / PriMan / Source / Window.c < prev   
C/C++ Source or Header  |  1994-11-13  |  46KB  |  1,361 lines

  1. /*
  2.  *        Task Priority Manager
  3.  *        Copyright 1993, 1994 Barry McConnell
  4.  *        bmccnnll@tcd.ie
  5.  *
  6.  *        Code to open the main window, settings window, and build the task list.
  7.  *
  8.  *        Set tab stops to 4 when editing this file.
  9.  */
  10.  
  11. #include "PriMan.h"
  12.  
  13. /*
  14.  *        Layout the gadgets for the main window, and open it.
  15.  */
  16. void OpenMainWindow(void)
  17.     {
  18.     WORD                buttonText,        /* width of Settings button                */
  19.                         sizeHeight,        /* height of window size gadget            */
  20.                         spacing,        /* spacing between buttons                */
  21.  
  22.                         /*
  23.                          *        The next few variables hold various window
  24.                          *        sizes, explained later.
  25.                          */
  26.                         listWidth, listHeight, gadHeight, width1, width2;
  27.  
  28.     BOOL                fontError;        /* already warned user about font error    */
  29.  
  30.     struct TextExtent    fontSize;        /* structure containing font size        */
  31.     struct RastPort        myRast;            /* used to check widths of text strings    */
  32.     struct DrawInfo        *drawInfo;        /* BOOPSI needs this                    */
  33.     struct Image        *sizeImage;        /* window size gadget image                */
  34.     struct NewGadget    myGad;            /* structure used when creating gadgets */
  35.     struct Gadget        *prevGad;        /* last gadget created                    */
  36.  
  37.     struct NewMenu menu[] =                /*     the main window's menu structure    */
  38.         {
  39.             { NM_TITLE,    "Project",            0,   0, 0, 0 },
  40.             { NM_ITEM,    "Settings...",        "S", 0, 0, 0 },
  41.             { NM_ITEM,    NM_BARLABEL,        0,   0, 0, 0 },
  42.             { NM_ITEM,    "About...",            "?", 0, 0, 0 },
  43.             { NM_ITEM,    "Help...",            0,     0, 0, 0 },
  44.             { NM_ITEM,    NM_BARLABEL,        0,   0, 0, 0 },
  45.             { NM_ITEM,    "Hide",                "H", 0, 0, 0 },
  46.             { NM_ITEM,    "Quit",                "Q", 0, 0, 0 },
  47.             { NM_TITLE, "Task",                0,   0, 0, 0 },
  48.             { NM_ITEM,    "Update List",        "U", 0, 0, 0 },
  49.             { NM_ITEM,    NM_BARLABEL,        0,   0, 0, 0 },
  50.             { NM_ITEM,    "Kill...",            "K", 0, 0, 0 },
  51.             { NM_ITEM,    NM_BARLABEL,        0,   0, 0, 0 },
  52.             { NM_ITEM,    "Signal...",        0,   0, 0, 0 },
  53.             { NM_SUB,    "Ctrl-C",            0,   0, 0, (APTR)SIGBREAKF_CTRL_C },
  54.             { NM_SUB,    "Ctrl-D",            0,   0, 0, (APTR)SIGBREAKF_CTRL_D },
  55.             { NM_SUB,    "Ctrl-E",            0,   0, 0, (APTR)SIGBREAKF_CTRL_E },
  56.             { NM_SUB,    "Ctrl-F",            0,   0, 0, (APTR)SIGBREAKF_CTRL_F },
  57.             { NM_ITEM,    "Priority",            0,   0, 0, 0 },
  58.             { NM_SUB,    "-5",                "-", 0, 0, (APTR)-5 },
  59.             { NM_SUB,    "-1",                0,     0, 0, (APTR)-1 },
  60.             { NM_SUB,    "0",                "=", 0, 0, (APTR)0 },
  61.             { NM_SUB,    "+1",                0,     0, 0, (APTR)+1 },
  62.             { NM_SUB,    "+5",                "+", 0, 0, (APTR)+5 },
  63.             { NM_ITEM,    NM_BARLABEL,        0,   0, 0, 0 },
  64.             { NM_ITEM,    "Frozen",            "F", CHECKIT | MENUTOGGLE, 0, 0 },
  65.             { NM_ITEM,    NM_BARLABEL,        0,   0, 0, 0 },
  66.             { NM_ITEM,    "Wide Slider",        "W", CHECKIT | MENUTOGGLE, 0, 0 },
  67.             { NM_END,    NULL,                0,   0, 0, 0 }
  68.         };
  69.  
  70.     /*
  71.      *        Message that appears when one of the fonts couldn't be opened.
  72.      */
  73.     struct EasyStruct font =
  74.         {
  75.         sizeof(struct EasyStruct),
  76.         0,
  77.         "PriMan trouble",
  78.         "Can't open %s -\ndefaulting to Topaz 8.",
  79.         "Okay"
  80.         };
  81.  
  82.     /*
  83.      *        Perform all the necessary stuff before opening any windows, such as
  84.      *        locking the public screen and getting the fonts. If the window is
  85.      *        already open, this step is not necessary.
  86.      */
  87.     if (!mainWindow)
  88.         {
  89.         if (myScreen = LockOurScreen(TRUE))
  90.             {
  91.             if ((visInfo) = GetVisualInfo(myScreen, TAG_END))
  92.                 {
  93.                 /*
  94.                  *        If the first character of the font name is 0, or the
  95.                  *        height is 0, it means the user never gave a font name on
  96.                  *        startup. In this case, we use the Preferences fonts.
  97.                  */
  98.                 if (!propName[0] || !propTA.ta_YSize)
  99.                     {
  100.                     strcpy(propName, myScreen -> Font -> ta_Name);
  101.                     propTA.ta_YSize = myScreen -> Font -> ta_YSize;
  102.                     }
  103.         
  104.                 if (!monoName[0] || !monoTA.ta_YSize)
  105.                     {
  106.                     strcpy(monoName, GfxBase -> DefaultFont -> tf_Message.mn_Node.ln_Name);
  107.                     monoTA.ta_YSize = GfxBase -> DefaultFont -> tf_YSize;
  108.                     }
  109.  
  110.                 /*
  111.                  *        Now we try to open the fonts. If either font fails to
  112.                  *        open, we attempt to fall back on Topaz 8. fontError is
  113.                  *        set if there is a problem with the Gadget font, so we
  114.                  *        know not to bother the user with a second requester if
  115.                  *        we can't open the List font either.
  116.                  */
  117.                 fontError = FALSE;
  118.  
  119.                 if (!(propFont = OpenDiskFont(&propTA)))
  120.                     {
  121.                     SimpleRequest(&font, propName);
  122.                     fontError = TRUE;
  123.  
  124.                     propTA.ta_YSize = 8;
  125.                     strcpy(propName, "topaz.font");
  126.                     propFont = OpenDiskFont(&propTA);
  127.                     }
  128.  
  129.                 if (!(monoFont = OpenDiskFont(&monoTA)))
  130.                     {
  131.                     if (!fontError)
  132.                         SimpleRequest(&font, monoName);
  133.  
  134.                     monoTA.ta_YSize = 8;
  135.                     strcpy(monoName, "topaz.font");
  136.                     monoFont = OpenDiskFont(&monoTA);
  137.                     }
  138.  
  139.                 /*
  140.                  *        If both fonts opened okay, we figure out their sizes in
  141.                  *        pixels. Else, we abort - but Topaz 8 *should* be around!
  142.                  */
  143.                 if (propFont && monoFont)
  144.                     {
  145.                     FontExtent(propFont, &fontSize);
  146.                     height = fontSize.te_Height;
  147.                     FontExtent(monoFont, &fontSize);
  148.                     sysHeight = fontSize.te_Height;
  149.                     sysWidth = fontSize.te_Width;
  150.  
  151.                     /*
  152.                      *        If this is the very first time the main window has
  153.                      *        been opened, and the user did not supply any default
  154.                      *        dimensions, we estimate a good size. The height will
  155.                      *        will be 2/3 the screen height, and the width will
  156.                      *        be half the height. The top and left positions will
  157.                      *        be chosen so as to centre the window on the screen.
  158.                      */
  159.                     if (!winHeight)
  160.                         winHeight = myScreen -> Height * 2 / 3;
  161.                     if (!winWidth)
  162.                         winWidth = winHeight / 2;
  163.                     if (winTop == -1)
  164.                         winTop = (myScreen -> Height - winHeight) / 2;
  165.                     if (winLeft == -1)
  166.                         winLeft = (myScreen -> Width - winWidth) / 2;
  167.                     }
  168.                 else
  169.                     error = FONT_ERROR;
  170.                 }
  171.             else
  172.                 error = VISINFO_ERROR;
  173.             }
  174.         else
  175.             error = LOCK_ERROR;
  176.         }
  177.  
  178.     if (!error)
  179.         {
  180.         /*
  181.          *        Calculate the width of the widest button. This is done using a
  182.          *        dummy RastPort for getting string lengths.
  183.          */
  184.         InitRastPort(&myRast);
  185.         myRast.Font = propFont;
  186.         buttonText    = TextLength(&myRast, "Settings...", 11) + INTERWIDTH * 2;
  187.     
  188.         /*
  189.          *        Calculate the height of the bottom window border. This is
  190.          *        dependent on the dimensions of the size gadget, which can
  191.          *        change using sysihack.
  192.          */
  193.         drawInfo = GetScreenDrawInfo(myScreen);
  194.         sizeImage = (struct Image *)NewObject(NULL, "sysiclass",
  195.                                                 SYSIA_DrawInfo,    drawInfo,
  196.                                                 SYSIA_Which,    SIZEIMAGE,
  197.                                                 SYSIA_Size,        SYSISIZE_MEDRES,
  198.                                                 TAG_END);
  199.         sizeHeight = sizeImage -> Height;
  200.         DisposeObject((APTR)sizeImage);
  201.         FreeScreenDrawInfo(myScreen, drawInfo);
  202.     
  203.         /*
  204.          *        Here we calculate the minimum window size we need to fit all
  205.          *        the gadgets. We need to see if the minimum space the ListView
  206.          *        needs is larger or smaller than the minimum space occupied by
  207.          *        the buttons.
  208.          *
  209.          *        windowTop is the size of the window's top border plus the title
  210.          *        bar font.
  211.          *
  212.          *        listWidth is the width of the window using a ListView with no
  213.          *        characters in it. It takes into account the gap needed between
  214.          *        the window borders on either side, and the width of the
  215.          *        ListView's own borders. A bug in 2.x clips the ListView text
  216.          *        too early (before the right border) when using certain fonts, so
  217.          *        we must allow for that.
  218.          *
  219.          *        listHeight allows for one or two elements in the ListView. It
  220.          *        takes into account the space occupied by the ListView's borders,
  221.          *        and the fact that under 2.x extra space is taken up by the name
  222.          *        of the currently-selected entry being displayed below.
  223.          *
  224.          *        gadHeight is the vertical space occupied by everything *else* in
  225.          *        the window, namely the slider gadget, the row of buttons, the
  226.          *        spacing between everything, and the size gadget in the bottom
  227.          *        border.
  228.          *
  229.          *        width1 is the width of the window using the smallest possible
  230.          *        ListView. It uses the ListView's "baggage" plus the space taken
  231.          *        by enough characters in it to contain the first letter of each
  232.          *        task name along with the priority. Worst case here would be a
  233.          *        frozen task (e.g. Workbench) running at a high priority: we need
  234.          *        to have enough room for "(W) -128", i.e. 8 characters.
  235.          *
  236.          *        width2 is simply the width of the three buttons with spacing
  237.          *        between them and on either side.
  238.          */
  239.         windowTop    = (myScreen -> WBorTop) + myScreen -> Font -> ta_YSize + 1;
  240.         listWidth    = INTERWIDTH + (osver < 39 ? ListEarlyClip : 0) + INTERWIDTH + ScrollBarWidth + INTERWIDTH;
  241.         listHeight    = INTERHEIGHT + sysHeight * (osver < 39 ? 4 : 2);
  242.         gadHeight     = windowTop + INTERHEIGHT + /* ListView */ INTERHEIGHT + (sysHeight + INTERHEIGHT) + INTERHEIGHT +
  243.                         (height + INTERHEIGHT) + INTERHEIGHT + sizeHeight;
  244.         width1        = sysWidth * 8 + listWidth;
  245.         width2        = INTERWIDTH + (buttonText + INTERWIDTH) * 3;
  246.         minWidth    = width1 > width2 ? width1 : width2;
  247.         minHeight    = gadHeight + listHeight;
  248.     
  249.         /*
  250.          *        Even using a 24-point font, a minimally-sized PriMan window can
  251.          *        still fit on a 640*200 screen. Therefore, there is no need to
  252.          *        ever drop down to Topaz 8 to "force" it to fit - making the
  253.          *        window smaller will always suffice. (Using a 50-point font on a
  254.          *        low-res screen would be plain silly!) Here we also make sure the
  255.          *        window is going to be at least as large as the minimum size
  256.          *        needed. If the window has already been opened, these lines are
  257.          *        not necessary since it's guaranteed to fit already, so instead
  258.          *        we take the opportunity to copy the physical window dimensions
  259.          *        into the variables.
  260.          */
  261.         if (mainWindow)
  262.             GetPos();
  263.         else
  264.             {
  265.             /*
  266.              *        Make sure window dimensions fall within the accepted range.
  267.              */
  268.             Range(&winWidth, minWidth, myScreen -> Width);
  269.             Range(&winHeight, minHeight, myScreen -> Height);
  270.             }
  271.     
  272.         /*
  273.          *        Now that we know the true width of the window we can, firstly,
  274.          *        calculate how many characters will fit in the ListView...
  275.          */
  276.         taskLength = (winWidth - listWidth) / sysWidth;
  277.     
  278.         /*
  279.          *        ...and, secondly, calculate the spacing between each button.
  280.          *        This is done by taking the window width, subtracting the amount
  281.          *        taken up by the buttons themselves and the border space, then
  282.          *        dividing by one less than the number of buttons on the row.
  283.          */
  284.         spacing = (winWidth - buttonText * 3 - INTERWIDTH * 2) / 2;
  285.     
  286.         /*
  287.          *        Now we have to create all the gadgets. We use prevGad to link
  288.          *        them together. If one call fails, then this will become NULL,
  289.          *        and the succeeding calls will automatically fail. Thus we need
  290.          *        only check prevGad after attempting to create all the gadgets.
  291.          */
  292.         mainGads    = NULL;
  293.         prevGad     = CreateContext(&mainGads);
  294.         
  295.         /*
  296.          *        Create the ListView.
  297.          */
  298.         myGad.ng_LeftEdge    = INTERWIDTH;
  299.         myGad.ng_TopEdge    = windowTop + INTERHEIGHT;  /* leave a gap below title bar */
  300.         myGad.ng_Width        = winWidth - INTERWIDTH * 2;  /* leave a gap on either side */
  301.         myGad.ng_Height     = winHeight - gadHeight;  /* maximum space left over for ListView */
  302.         myGad.ng_GadgetText = NULL;
  303.         myGad.ng_TextAttr    = &monoTA;
  304.         myGad.ng_GadgetID    = LISTGAD;
  305.         myGad.ng_Flags        = 0;
  306.         myGad.ng_VisualInfo = visInfo;
  307.         
  308.         prevGad = listGad = CreateGadget(LISTVIEW_KIND, prevGad, &myGad,
  309.                                             GTLV_Labels,            NULL,
  310.                                             GTLV_ShowSelected,        NULL,
  311.                                             TAG_END);
  312.     
  313.         /*
  314.          *        The ListView may get shrunk slightly, to accomodate an exact
  315.          *        number of lines. If the window has not already been opened, we
  316.          *        can check the created ListView's height, and shrink the window
  317.          *        appropriately. Under 2.x, the correct height does not get filled
  318.          *        into the Gadget structure, so this only works under 3.x.
  319.          */
  320.         if (!(mainWindow || osver < 39))
  321.             {
  322.             winHeight += (listGad) -> Height - myGad.ng_Height;
  323.             myGad.ng_Height = (listGad) -> Height;
  324.             }
  325.     
  326.         /*
  327.          *        Create the slider. Even though it gets enabled or disabled as
  328.          *        necessary later on, we assume the currently-selected task (if
  329.          *        any) will remain valid, and enable or disable it as necessary
  330.          *        here. This prevents it "flashing" when the window gets resized.
  331.          */
  332.         myGad.ng_LeftEdge    += sysWidth * 4 + INTERWIDTH;  /* allow room for the label */
  333.         myGad.ng_TopEdge    += myGad.ng_Height + INTERHEIGHT;
  334.         myGad.ng_Width        = winWidth - INTERWIDTH - myGad.ng_LeftEdge;
  335.         myGad.ng_Height     = sysHeight + INTERHEIGHT;
  336.         myGad.ng_GadgetID    = SLIDERGAD;
  337.         
  338.         prevGad = sliderGad = CreateGadget(SLIDER_KIND, prevGad, &myGad,
  339.                                             GTSL_Min,            -25,
  340.                                             GTSL_Max,            25,
  341.                                             GTSL_Level,         0,
  342.                                             GTSL_LevelFormat,    "%4ld",
  343.                                             GTSL_MaxLevelLen,    4,
  344.                                             GTSL_LevelPlace,    PLACETEXT_LEFT,
  345.                                             GA_RelVerify,        TRUE,
  346.                                             GA_Disabled,        pos == -1,
  347.                                             TAG_END);
  348.         
  349.         /*
  350.          *        Create the Break button.
  351.          */
  352.         myGad.ng_LeftEdge    = INTERWIDTH;
  353.         myGad.ng_TopEdge    += myGad.ng_Height + INTERHEIGHT;
  354.         myGad.ng_Width        = buttonText;
  355.         myGad.ng_Height        = height + INTERHEIGHT;
  356.         myGad.ng_GadgetText = "_Break";
  357.         myGad.ng_TextAttr    = &propTA;
  358.         myGad.ng_GadgetID    = BREAKGAD;
  359.         
  360.         prevGad = breakGad = CreateGadget(BUTTON_KIND, prevGad, &myGad,
  361.                                             GT_Underscore,    '_',
  362.                                             GA_Disabled,    pos == -1,
  363.                                             TAG_END);
  364.     
  365.         /*
  366.          *        Create the Kill button, using the spacing variable to decide how
  367.          *        far right it goes relative to the Break button.
  368.          */
  369.         myGad.ng_LeftEdge    += buttonText + spacing;
  370.         myGad.ng_GadgetText = "_Kill";
  371.         myGad.ng_GadgetID    = KILLGAD;
  372.         
  373.         prevGad = killGad = CreateGadget(BUTTON_KIND, prevGad, &myGad,
  374.                                             GT_Underscore,    '_',
  375.                                             GA_Disabled,    pos == -1,
  376.                                             TAG_END);
  377.     
  378.         /*
  379.          *        Create the Settings button. This always goes relative to the
  380.          *        right window border.
  381.          */
  382.         myGad.ng_LeftEdge    = winWidth - INTERWIDTH - buttonText;
  383.         myGad.ng_GadgetText = "_Settings...";
  384.         myGad.ng_GadgetID    = SETTINGSGAD;
  385.         
  386.         prevGad = setGad = CreateGadget(BUTTON_KIND, prevGad, &myGad,
  387.                                 GT_Underscore,    '_',
  388.                                 TAG_END);
  389.     
  390.         /*
  391.          *        Once we've got this far, we need to check if all the gadgets
  392.          *        actually got created. Next, if the window was was already open
  393.          *        (i.e. this function was called because of a resize), any
  394.          *        remaining imagery inside its borders will need clearing, and we
  395.          *        can simply add the gadgets straight in. The alternative requires
  396.          *        opening the window and attaching the menu strip.
  397.          */
  398.         if (prevGad)
  399.             {
  400.             if (mainWindow)
  401.                 {
  402.                 /*
  403.                  *        When clearing the window's old imagery, everything
  404.                  *        should be wiped except the following:
  405.                  *
  406.                  *        - Title bar and space underneath it
  407.                  *        - Left border and space inside it
  408.                  *        - Right border
  409.                  *        - Bottom border
  410.                  */
  411.                 blank.Width = winWidth - INTERWIDTH - INTERWIDTH / 2;
  412.                 blank.Height = winHeight - windowTop - INTERHEIGHT - sizeHeight;
  413.                 EraseImage(mainWindow -> RPort, &blank, INTERWIDTH, windowTop + INTERHEIGHT);
  414.     
  415.                 AddGList(mainWindow, mainGads, ~0, -1, NULL);
  416.                 RefreshGList(mainGads, mainWindow, NULL, -1);
  417.                 GT_RefreshWindow(mainWindow, NULL);
  418.                 CreateList(&memoryKey);
  419.                 }
  420.             else
  421.                 {    
  422.                 if (mainWindow = OpenWindowTags(NULL,
  423.                                                 WA_Title,            "PriMan",
  424.                                                 WA_ScreenTitle,        "Task Priority Manager "VERSION" - Copyright 1994 Barry McConnell",
  425.                                                 WA_Gadgets,         mainGads,
  426.                                                 WA_Left,            winLeft,
  427.                                                 WA_Top,             winTop,
  428.                                                 WA_Width,            winWidth,
  429.                                                 WA_Height,            winHeight,
  430.                                                 WA_MinWidth,        minWidth,
  431.                                                 WA_MinHeight,        minHeight,
  432.                                                 WA_MaxWidth,        ~0,
  433.                                                 WA_MaxHeight,        ~0,
  434.                                                 WA_DragBar,         TRUE,
  435.                                                 WA_DepthGadget,     TRUE,
  436.                                                 WA_CloseGadget,     TRUE,
  437.                                                 WA_SizeGadget,        TRUE,
  438.                                                 WA_SizeBBottom,        TRUE,
  439.                                                 WA_Activate,        TRUE,
  440.                                                 WA_NewLookMenus,    TRUE,
  441.     refresh == SIMPLEWINDOW ? WA_SimpleRefresh : WA_SmartRefresh,    TRUE,
  442.                                                 WA_PubScreen,        myScreen,
  443.                                                 TAG_END))
  444.                     {
  445.                     if (menuStrip = CreateMenus(menu, TAG_END))
  446.                         {
  447.                         /*
  448.                          *        We want to get the initial state of the "..."
  449.                          *        after the Kill and Signal menu items right, so
  450.                          *        we use this function, which also attaches the
  451.                          *        menu strip to our window. If there is an error
  452.                          *        here, the tidyup routine will free up the menu
  453.                          *        strip for us, since the main window is already
  454.                          *        open.
  455.                          */
  456.                         MenuEllipsis(menuStrip, FALSE);
  457.                         if (!error)
  458.                             {
  459.                             /*
  460.                              *        If we're not running as a Commodity and
  461.                              *        can't be iconified, there is no point in
  462.                              *        allowing the user to select the Hide menu
  463.                              *        item!
  464.                              */
  465.                             if (!(iconify || commodity))
  466.                                 OffMenu(mainWindow, FULLMENUNUM(M_PROJECT, I_HIDE, NOSUB));
  467.  
  468.                             /*
  469.                              *        Set up the message port, refresh the window,
  470.                              *        add in the task list, and make sure our
  471.                              *        screen is visible.
  472.                              */
  473.                             mainWindow -> UserPort = winPort;
  474.                             ModifyIDCMP(mainWindow, WINDOWIDCMP);
  475.                             GT_RefreshWindow(mainWindow, NULL);
  476.                             CreateList(&memoryKey);
  477.                             ScreenToFront(myScreen);
  478.                             }
  479.                         }
  480.                     else
  481.                         error = MENU_ERROR;
  482.                     }
  483.                 else
  484.                     error = MAIN_ERROR;
  485.                 }
  486.             }
  487.         else
  488.             error = GADGET_ERROR;
  489.         }
  490.     }
  491.  
  492.  
  493. /*
  494.  *        Layout the gadgets for the settings window, and open it. There are
  495.  *        actually going to be three sets of gadgets for this window, with the
  496.  *        user selecting one set at a time. So we create all three sets here, and
  497.  *        then use the one which the user had selected the last time this window
  498.  *        was open.
  499.  */
  500. void OpenSettingsWindow(void)
  501.     {
  502.     WORD                oldPage,    /* last gadget page showing when settings window was opened    */
  503.  
  504.                         /*
  505.                          *        These relate to the size of the checkbox gadget,
  506.                          *        and are explained later.
  507.                          */
  508.                         size, offset, max,
  509.  
  510.                         /*
  511.                          *        These relate to the window dimensions, and are
  512.                          *        also explained later.
  513.                          */
  514.                         buttonWidth, textWidth, cancelWidth, spacing, intWidth, innerWidth, setWidth,
  515.  
  516.                         /*
  517.                          *        End of each page of gadgets. gadgetEnd itself is
  518.                          *        the lowest position the gadgets reach to.
  519.                          */
  520.                         gadgetEnd1, gadgetEnd2, gadgetEnd3, gadgetEnd;
  521.  
  522.     struct RastPort        myRast;        /* used to check widths of text strings                        */
  523.     struct NewGadget    myGad;        /* structure used when creating gadgets                     */
  524.     struct Gadget        *prevGad1,    /* last gadget created - one for each page of gadgets        */
  525.                         *prevGad2, *prevGad3, *prevGad4;
  526.  
  527.     /*
  528.      *        These contain the text for the three cycle gadgets.
  529.      */
  530.     static char            *pageText[]        = { "Interface", "Commodity", "General", NULL };
  531.     static char            *refreshText[]    = { "Smart Refresh", "Simple Refresh", NULL };
  532.      static char            *openText[]        = { "Default Screen", "Front Screen", NULL };
  533.  
  534.     /*
  535.      *        Here we remember the original settings of everything, so they can be
  536.      *        restored if the user clicks Cancel.
  537.      */
  538.     newPropFont        = newMonoFont = FALSE;
  539.     tempRefresh        = refresh;
  540.     tempOpen        = open;
  541.     tempConfirm        = confirm;
  542.     tempIconify        = iconify;
  543.     tempCommodity    = commodity;
  544.     tempPopup        = popup;
  545.  
  546.     FontString(&propTA, propString);  /* initial setting for the text box */
  547.     FontString(&monoTA, monoString);
  548.  
  549.     strcpy(tempPropName, propName);  /* initial settings for the font requesters */
  550.     strcpy(tempMonoName, monoName);
  551.     tempPropSize = propTA.ta_YSize;
  552.     tempMonoSize = monoTA.ta_YSize;
  553.  
  554.     InitRastPort(&myRast);
  555.     myRast.Font = propFont;
  556.  
  557.     /*
  558.      *        Under 3.x, we can scale the checkboxes to match the font size. The
  559.      *        following variables are used:
  560.      *
  561.      *        size holds the actual height of the checkbox. It takes into account
  562.      *        whether or not it can be scaled, and has a minimum size.
  563.      *
  564.      *        offset is non-zero if the font is taller than the checkbox, and
  565.      *        gives the extra amount the checkbox will need to be moved down to be
  566.      *        centred with respect to its label.
  567.      *
  568.      *        max is the actual height taken by the checkbox and label
  569.      *        side-by-side, and is just the maximum of their respective heights,
  570.      *        used for lining up gadgetry below them.
  571.      */
  572.     size        = osver < 39 ? 11 : (height > 10 ? height : 11);
  573.     offset        = height > size ? (height - size) / 2 : 0;
  574.     max            = height > size ? height : size;
  575.     
  576.     /*
  577.      *        Now we calculate the widths of a few things, to help us lay out the
  578.      *        interface:
  579.      *
  580.      *        buttonWidth is the width of each Font button.
  581.      *
  582.      *        textWidth is the width of the text boxes beside the buttons. For
  583.      *        want of anything better to base this on, we'll use the width of the
  584.      *        two cycle gadget below it for this.
  585.      *
  586.      *        cancelWidth is the width of the Save, Use and Cancel buttons.
  587.      *
  588.      *        spacing is the distance between these three buttons.
  589.      *
  590.      *        intWidth is the size of any integer boxes (enough to hold "-128"
  591.      *        plus the cursor).
  592.      *
  593.      *        innerWidth is the width of the portion of the window inside the
  594.      *        bevel box, allowing a gap.
  595.      *
  596.      *        setWidth is the width of the whole window, basically just allowing
  597.      *        a gap for the bevel box.
  598.      */
  599.     buttonWidth    = TextLength(&myRast, "Gadget Font...", 14) + INTERWIDTH * 2;
  600.     textWidth    = TextLength(&myRast, "Simple Refresh", 14) + INTERWIDTH * 2 + CycleWidth;
  601.     cancelWidth    = TextLength(&myRast, "Cancel", 6) + INTERWIDTH * 2;
  602.     intWidth    = TextLength(&myRast, "-9999", 5) + INTERWIDTH * 2;
  603.     innerWidth    = INTERWIDTH + buttonWidth + INTERWIDTH + textWidth + INTERWIDTH;
  604.     setWidth    = INTERWIDTH + innerWidth + INTERWIDTH;
  605.     spacing        = (setWidth - INTERWIDTH * 2 - cancelWidth * 3) / 2;
  606.  
  607.     /*
  608.      *        Create and link all the gadgets, as before. Each set of gadgets is
  609.      *        pointed to from an array.
  610.      */    
  611.     setGads[3] = NULL;
  612.     prevGad1 = CreateContext(&(setGads[3]));
  613.     
  614.     /*
  615.      *         Create the cycle gadget at the top of the window. This is stretched
  616.      *        to be as wide as possible.
  617.      */
  618.     myGad.ng_LeftEdge    = INTERWIDTH + TextLength(&myRast, "Page", 4) + INTERWIDTH;
  619.     myGad.ng_TopEdge    = windowTop + INTERHEIGHT;
  620.     myGad.ng_Width        = setWidth - myGad.ng_LeftEdge - INTERWIDTH;
  621.     myGad.ng_Height        = height + INTERHEIGHT + 2;
  622.     myGad.ng_GadgetText = "_Page";
  623.     myGad.ng_GadgetID    = PAGEGAD;
  624.     myGad.ng_TextAttr    = &propTA;
  625.     myGad.ng_Flags        = PLACETEXT_LEFT;
  626.     myGad.ng_VisualInfo = visInfo;
  627.  
  628.     prevGad1 = pageGad = CreateGadget(CYCLE_KIND, prevGad1, &myGad,
  629.                                         GT_Underscore,    '_',
  630.                                         GTCY_Labels,    pageText,
  631.                                         GTCY_Active,    page,
  632.                                         TAG_END);
  633.  
  634.     setGadStart = myGad.ng_TopEdge + myGad.ng_Height + INTERHEIGHT * 3;
  635.  
  636.     /*
  637.      *        We want a separate set of gadgets for Interface.
  638.      */
  639.     setGads[0] = NULL;
  640.     prevGad2 = CreateContext(&(setGads[0]));
  641.  
  642.     /*
  643.      *        Create the Gadget Font button.
  644.      */
  645.     myGad.ng_LeftEdge    = INTERWIDTH * 2;
  646.     myGad.ng_TopEdge    = setGadStart;
  647.     myGad.ng_Width        = buttonWidth;
  648.     myGad.ng_Height     = height + INTERHEIGHT;
  649.     myGad.ng_GadgetText = "_Gadget Font...";
  650.     myGad.ng_GadgetID    = GFONTGAD;
  651.     myGad.ng_Flags        = 0;
  652.     
  653.     prevGad2 = propFontButGad = CreateGadget(BUTTON_KIND, prevGad2, &myGad,
  654.                                         GT_Underscore,    '_',
  655.                                         TAG_END);
  656.  
  657.     /*
  658.      *        Create the Gadget Font text box.
  659.      */
  660.     myGad.ng_LeftEdge    += myGad.ng_Width + INTERWIDTH;
  661.     myGad.ng_Width        = textWidth;
  662.     myGad.ng_GadgetText = NULL;
  663.     myGad.ng_GadgetID    = GBOXGAD;
  664.     
  665.     prevGad2 = propFontGad = CreateGadget(TEXT_KIND, prevGad2, &myGad,
  666.                                             GTTX_Border,    TRUE,
  667.                                             GTTX_Text,        propString,
  668.                                             TAG_END);
  669.  
  670.     /*
  671.      *        Create the List Font button.
  672.      */
  673.     myGad.ng_LeftEdge    = INTERWIDTH * 2;
  674.     myGad.ng_TopEdge    += myGad.ng_Height + INTERHEIGHT;
  675.     myGad.ng_Width        = buttonWidth;
  676.     myGad.ng_GadgetText = "_List Font...";
  677.     myGad.ng_GadgetID    = LFONTGAD;
  678.     
  679.     prevGad2 = monoFontButGad = CreateGadget(BUTTON_KIND, prevGad2, &myGad,
  680.                                                 GT_Underscore,    '_',
  681.                                                 TAG_END);
  682.  
  683.     /*
  684.      *        Create the List Font text box.
  685.      */
  686.     myGad.ng_LeftEdge    += myGad.ng_Width + INTERWIDTH;
  687.     myGad.ng_Width        = textWidth;
  688.     myGad.ng_GadgetText = NULL;
  689.     myGad.ng_GadgetID    = LBOXGAD;
  690.     
  691.     prevGad2 = monoFontGad = CreateGadget(TEXT_KIND, prevGad2, &myGad,
  692.                                             GTTX_Border,    TRUE,
  693.                                             GTTX_Text,        monoString,
  694.                                             TAG_END);
  695.  
  696.     /*
  697.      *        Create the Refresh cycle gadget, lined up just below the text boxes
  698.      *        above.
  699.      */
  700.     myGad.ng_TopEdge    += myGad.ng_Height + INTERHEIGHT;
  701.     myGad.ng_Height        += 2;  /* looks better with an extra couple of pixels! */
  702.     myGad.ng_GadgetText    = "_Window Type";
  703.     myGad.ng_GadgetID    = REFRESHGAD;
  704.     myGad.ng_Flags        = PLACETEXT_LEFT;
  705.     
  706.     prevGad2 = refreshGad = CreateGadget(CYCLE_KIND, prevGad2, &myGad,
  707.                                             GT_Underscore,    '_',
  708.                                             GTCY_Labels,    refreshText,
  709.                                             GTCY_Active,    refresh,
  710.                                             TAG_END);
  711.  
  712.     /*
  713.      *        Create the Open On cycle gadget, lining it up as above.
  714.      */
  715.     myGad.ng_TopEdge    += myGad.ng_Height + INTERHEIGHT;
  716.     myGad.ng_GadgetText    = "_Open On";
  717.     myGad.ng_GadgetID    = OPENGAD;
  718.     
  719.     prevGad2 = openGad = CreateGadget(CYCLE_KIND, prevGad2, &myGad,
  720.                                         GT_Underscore,    '_',
  721.                                         GTCY_Labels,    openText,
  722.                                         GTCY_Active,    open,
  723.                                         TAG_END);
  724.  
  725.  
  726.     gadgetEnd1 = myGad.ng_TopEdge + myGad.ng_Height;
  727.  
  728.     /*
  729.      *        Here's a new set of gadgets for Commodity.
  730.      */
  731.     setGads[1] = NULL;
  732.     prevGad4 = CreateContext(&(setGads[1]));
  733.  
  734.     /*
  735.      *        Create the Install checkbox.
  736.      */
  737.     myGad.ng_LeftEdge    = INTERWIDTH * 2;
  738.     myGad.ng_TopEdge    = setGadStart + offset;
  739.     myGad.ng_Width        = 26;
  740.     myGad.ng_Height        = size;
  741.     myGad.ng_GadgetText    = "_Install as a Commodity";
  742.     myGad.ng_GadgetID    = COMGAD;
  743.     myGad.ng_Flags        = PLACETEXT_RIGHT;
  744.     
  745.     prevGad4 = comGad = CreateGadget(CHECKBOX_KIND, prevGad4, &myGad,
  746.                                         GT_Underscore,    '_',
  747.                                         GTCB_Scaled,    TRUE,
  748.                                         GTCB_Checked,    commodity,
  749.                                         TAG_END);
  750.  
  751.     /*
  752.      *        Create the Popup checkbox. Disable it (and the rest) if PriMan is
  753.      *        not running as a Commodity.
  754.      */
  755.     myGad.ng_TopEdge    += max + INTERHEIGHT;  /* offset was already added in for us */
  756.     myGad.ng_GadgetText = "Popup When _Launched";
  757.     myGad.ng_GadgetID    = POPUPGAD;
  758.     
  759.     prevGad4 = popupGad = CreateGadget(CHECKBOX_KIND, prevGad4, &myGad,
  760.                                         GT_Underscore,    '_',
  761.                                         GTCB_Scaled,    TRUE,
  762.                                         GTCB_Checked,    popup,
  763.                                         GA_Disabled,    !commodity,
  764.                                         TAG_END);
  765.     
  766.     /*
  767.      *        Create the Hotkey text box.
  768.      */
  769.     myGad.ng_LeftEdge    += TextLength(&myRast, "Priority", 8) + INTERWIDTH;
  770.     myGad.ng_TopEdge    += max - offset + INTERHEIGHT;
  771.     myGad.ng_Width        = innerWidth - myGad.ng_LeftEdge;
  772.     myGad.ng_Height        = height + INTERHEIGHT + 2;
  773.     myGad.ng_GadgetText    = "_Hotkey";
  774.     myGad.ng_GadgetID    = HOTKEYGAD;
  775.     myGad.ng_Flags        = PLACETEXT_LEFT;
  776.     
  777.     prevGad4 = hotkeyGad = CreateGadget(STRING_KIND, prevGad4, &myGad,
  778.                                         GT_Underscore,    '_',
  779.                                         GTST_String,    hotkey,
  780.                                         GTST_MaxChars,    MaxHotkey,
  781.                                         GA_Disabled,    !commodity,
  782.                                         TAG_END);
  783.  
  784.     /*
  785.      *        Create the Commodity Priority integer box.
  786.      */
  787.     myGad.ng_TopEdge    += myGad.ng_Height + INTERHEIGHT;
  788.     myGad.ng_Width        = intWidth;
  789.     myGad.ng_GadgetText = "Priorit_y";
  790.     myGad.ng_GadgetID    = PRIORITYGAD;
  791.     
  792.     prevGad4 = priorityGad = CreateGadget(INTEGER_KIND, prevGad4, &myGad,
  793.                                             GT_Underscore,    '_',
  794.                                             GTIN_Number,    priority,
  795.                                             GTIN_MaxChars,    4,  /* e.g., -128 */
  796.                                             GA_Disabled,    !commodity,
  797.                                             TAG_END);
  798.  
  799.     gadgetEnd3 = myGad.ng_TopEdge + myGad.ng_Height;
  800.  
  801.     /*
  802.      *        Here's a new set of gadgets for General.
  803.      */
  804.     setGads[2] = NULL;
  805.     prevGad3 = CreateContext(&(setGads[2]));
  806.  
  807.     /*
  808.      *        Create the Task Priority integer box, initialising it to our current
  809.      *        priority.
  810.      */
  811.     myGad.ng_LeftEdge    = INTERWIDTH * 2 + TextLength(&myRast, "Task Priority", 13) + INTERWIDTH;
  812.     myGad.ng_TopEdge    = setGadStart;
  813.     myGad.ng_GadgetText = "Task Priorit_y";
  814.     myGad.ng_GadgetID    = TOOLPRIGAD;
  815.     
  816.     prevGad3 = toolpriGad = CreateGadget(INTEGER_KIND, prevGad3, &myGad,
  817.                                             GT_Underscore,    '_',
  818.                                             GTIN_Number,    FindTask(NULL) -> tc_Node.ln_Pri,
  819.                                             GTIN_MaxChars,    4,  /* e.g., -128 */
  820.                                             TAG_END);
  821.  
  822.     /*
  823.      *        Create the Confirm Actions checkbox.
  824.      */
  825.     myGad.ng_LeftEdge    = INTERWIDTH * 2;
  826.     myGad.ng_TopEdge    += myGad.ng_Height + INTERHEIGHT + offset;
  827.     myGad.ng_Width        = 26;
  828.     myGad.ng_Height        = size;
  829.     myGad.ng_GadgetText    = "Confirm _Actions";
  830.     myGad.ng_GadgetID    = CONFIRMGAD;
  831.     myGad.ng_Flags        = PLACETEXT_RIGHT;
  832.     
  833.     prevGad3 = confirmGad = CreateGadget(CHECKBOX_KIND, prevGad3, &myGad,
  834.                                             GT_Underscore,    '_',
  835.                                             GTCB_Scaled,    TRUE,  /* new in 3.0 */
  836.                                             GTCB_Checked,    confirm,
  837.                                             TAG_END);
  838.  
  839.     /*
  840.      *        Create the Iconify checkbox.
  841.      */
  842.     myGad.ng_TopEdge    += max + INTERHEIGHT;
  843.     myGad.ng_GadgetText = "_Iconify When Closed";
  844.     myGad.ng_GadgetID    = ICONIFYGAD;
  845.     
  846.     prevGad3 = iconifyGad = CreateGadget(CHECKBOX_KIND, prevGad3, &myGad,
  847.                                             GT_Underscore,    '_',
  848.                                             GTCB_Scaled,    TRUE,
  849.                                             GTCB_Checked,    iconify,
  850.                                             TAG_END);
  851.  
  852.     gadgetEnd2 = myGad.ng_TopEdge + max - offset;
  853.  
  854.     /*
  855.      *        Now that we have laid out the three sets of gadgets, we know exactly
  856.      *        how big the window must be. We calculate the true end of the
  857.      *        gadgets, and then place the Save, Use & Cancel buttons below this.
  858.      */
  859.     gadgetEnd        = gadgetEnd1 > gadgetEnd2 ? gadgetEnd1 : gadgetEnd2;
  860.     gadgetEnd        = gadgetEnd > gadgetEnd3 ? gadgetEnd : gadgetEnd3;
  861.     setGadHeight    = gadgetEnd - setGadStart;
  862.  
  863.     /*
  864.      *        Create the Save button. Disable it if PriMan's .info file cannot be
  865.      *        found, since we won't have any icon in which to store the ToolTypes!
  866.      */
  867.     myGad.ng_LeftEdge    = INTERWIDTH;
  868.     myGad.ng_TopEdge    = gadgetEnd + INTERHEIGHT * 3;
  869.     myGad.ng_Width        = cancelWidth;
  870.     myGad.ng_Height        = height + INTERHEIGHT;
  871.     myGad.ng_GadgetText = "_Save";
  872.     myGad.ng_Flags        = 0;
  873.     myGad.ng_GadgetID    = SAVEGAD;
  874.     
  875.     prevGad1 = saveGad = CreateGadget(BUTTON_KIND, prevGad1, &myGad,
  876.                                         GT_Underscore,    '_',
  877.                                         GA_Disabled,    myIcon == NULL,
  878.                                         TAG_END);
  879.  
  880.     /*
  881.      *        Create the Use button, evenly distributing the spacing between the
  882.      *        three buttons.
  883.      */
  884.     myGad.ng_LeftEdge    += myGad.ng_Width + spacing;
  885.     myGad.ng_GadgetText = "_Use";
  886.     myGad.ng_GadgetID    = USEGAD;
  887.     
  888.     prevGad1 = useGad = CreateGadget(BUTTON_KIND, prevGad1, &myGad,
  889.                                         GT_Underscore,    '_',
  890.                                         TAG_END);
  891.  
  892.     /*
  893.      *        Create the Cancel button.
  894.      */
  895.     myGad.ng_LeftEdge    += cancelWidth + (setWidth - INTERWIDTH * 2 - cancelWidth * 3) / 2;
  896.     myGad.ng_GadgetText = "_Cancel";
  897.     myGad.ng_GadgetID    = CANCELGAD;
  898.     
  899.     prevGad1 = cancelGad = CreateGadget(BUTTON_KIND, prevGad1, &myGad,
  900.                                         GT_Underscore,    '_',
  901.                                         TAG_END);
  902.  
  903.     if (prevGad1 && prevGad2 && prevGad3 && prevGad4)
  904.         {
  905.         /*
  906.          *        The Settings window is offset from the main window a little -
  907.          *        moved down and across by the dimensions of the window's close
  908.          *        gadget.
  909.          */
  910.         if (setWindow = OpenWindowTags(NULL,
  911.                                         WA_Title,            "PriMan Settings",
  912.                                         WA_Gadgets,         setGads[3],
  913.                                         WA_Left,            mainWindow -> LeftEdge + CloseBoxWidth,
  914.                                         WA_Top,             mainWindow -> TopEdge + windowTop,
  915.                                         WA_Width,            setWidth,
  916.                                         WA_Height,            myGad.ng_TopEdge + myGad.ng_Height + INTERHEIGHT + myScreen -> WBorBottom,
  917.                                         WA_DragBar,         TRUE,
  918.                                         WA_DepthGadget,     TRUE,
  919.                                         WA_Activate,        TRUE,
  920.                                         WA_RMBTrap,         TRUE,
  921.                 refresh ? WA_SimpleRefresh : WA_SmartRefresh,    TRUE,
  922.                                         WA_PubScreen,        myScreen,
  923.                                         TAG_END))
  924.             {
  925.             setWindow -> UserPort = winPort;
  926.             ModifyIDCMP(setWindow, WINDOWIDCMP);
  927.             DrawSettingsBox();
  928.             GT_RefreshWindow(setWindow, NULL);
  929.             oldPage = page;  /* take a copy of the gadget page that was last showing */
  930.             page = -1;  /* next function will see that no gadgets are currently visible */
  931.             NewPage(oldPage);  /* display whatever the user last saw */
  932.             }
  933.         else
  934.             error = SETTINGS_ERROR;
  935.         }
  936.     else
  937.         error = GADGET_ERROR;
  938.     }
  939.  
  940.  
  941. /*
  942.  *        Generate the task list, and place it in the main window's ListView. If
  943.  *        the task has an appropriate CLI number, that will be added in before its
  944.  *        name. If it is frozen, its name will be bracketed. If a task was already
  945.  *        selected, we will attempt to keep it selected.
  946.  */
  947. void CreateList(struct Remember **memoryKey)
  948.     {
  949.     int                    i;                        /* task count                    */
  950.  
  951.     struct Node            *execNode;                /* index into Exec's task list    */
  952.     struct ListType        *taskNode;                /* storage space for task info    */
  953.     struct Task            *taskArray[MaxTasks];    /* pointers to task info        */
  954.  
  955.     /*
  956.      *        The main window's titlebar is actually set here.
  957.      */
  958.     static char            *title = "PriMan - 999 tasks";
  959.  
  960.     /*
  961.      *        This is the error message the user sees if there are more tasks in
  962.      *        the system than our hard-coded maximum (very unlikely!).
  963.      */
  964.     struct EasyStruct task =
  965.         {
  966.         sizeof(struct EasyStruct),
  967.         0,
  968.         "PriMan trouble",
  969.         "Too many tasks in system -\nlist may be incomplete.",
  970.         "Okay"
  971.         };
  972.  
  973.     /*
  974.      *        If we haven't yet established what the top entry in the ListView was
  975.      *        (top is -1), now would be a good time. (The code that handles window
  976.      *        resizing gets the top itself since it causes the whole ListView to
  977.      *        be freed up before it gets this far.) If this is our first time
  978.      *        here, top will be 0 which is what we want anyway.
  979.      */
  980.     if (top == -1)
  981.         GetListTop();
  982.  
  983.     /*
  984.      *        Here we remove the old task list from the ListView, and free up the
  985.      *        memory it occupied (we can do the freeing even if the memory key was
  986.      *        NULL already).
  987.      */
  988.     GT_SetGadgetAttrs(listGad, mainWindow, NULL,
  989.                         GTLV_Labels,    ~0,
  990.                         TAG_END);
  991.     FreeRemember(memoryKey, TRUE);
  992.  
  993.     /*
  994.      *        We are going to do three passes through the task list:
  995.      *
  996.      *        1. Fill taskArray with a list of task addresses. We do this while
  997.      *           interrupts are disabled, so the pointers remain valid and the
  998.      *           task lists don't get modified. Hence it must happen very fast!
  999.      *           The very first entry will be this task, since it doesn't appear
  1000.      *           on either the Ready or Waiting queues. taskCount is the next
  1001.      *           entry to be filled in the array.
  1002.      *
  1003.      *        2. Copy the task names and priorities into individual columnised
  1004.      *           strings inside ListType structures, using the pointers we placed
  1005.      *           in taskArray above. This happens while interrupts are enabled
  1006.      *           but multitasking disabled, so that vital things such as timer and
  1007.      *           serial interrupts can happen. Since multitasking is disabled, no
  1008.      *           tasks can actually exit (so our pointers remain valid), although
  1009.      *           it is possible for the task lists to be resorted (something which
  1010.      *           we don't need to worry about since we already walked them above).
  1011.      *
  1012.      *        3. After enabling multitasking, we can set about the time-consuming
  1013.      *           business of sorting the task list. Since we already have the
  1014.      *           names and priorities, we don't care what happens to the physical
  1015.      *           tasks from here on.
  1016.      *
  1017.      *        Here is the first pass...
  1018.      */
  1019.     taskArray[0] = FindTask(NULL);
  1020.     taskCount = 1;
  1021.     
  1022.     /*
  1023.      *        Remember that we want to disable interrupts during the first
  1024.      *        pass, and just multitasking during the second pass. So we'll
  1025.      *        nest the Forbid() and Disable() statements, allowing us to
  1026.      *        simply do an Enable() after the first pass to enable interrupts,
  1027.      *        but leave multitasking disabled.
  1028.      */
  1029.     Forbid();
  1030.     Disable();
  1031.  
  1032.     /*
  1033.      *        Here we walk both the Ready and Waiting queues, copying the task
  1034.      *        pointers in each into the array. The loop can prematurely abort
  1035.      *        if we reach the maximum allowable number of tasks (array size).
  1036.      */
  1037.     for (execNode = SysBase -> TaskReady.lh_Head;
  1038.             execNode -> ln_Succ && taskCount < MaxTasks;
  1039.             execNode = execNode -> ln_Succ)
  1040.         taskArray[taskCount++] = (struct Task *)execNode;
  1041.  
  1042.     for (execNode = SysBase -> TaskWait.lh_Head;
  1043.          execNode -> ln_Succ && taskCount < MaxTasks;
  1044.          execNode = execNode -> ln_Succ)
  1045.         taskArray[taskCount++] = (struct Task *)execNode;
  1046.  
  1047.     /*
  1048.      *        This Enable() will allow interrupts to continue, but not
  1049.      *        multitasking (remember we also did a Forbid()), so the data in
  1050.      *        the task lists will remain static.
  1051.      */    
  1052.     Enable();
  1053.  
  1054.     /*
  1055.      *        We're now onto the second pass. We allocate a Node for each task
  1056.      *        processed (we actually use our ListType structure, which is a
  1057.      *        superset of a Node), and enough memory to fit taskLength characters
  1058.      *        which is where our complete task name and priority (two columns)
  1059.      *        will be put. We use a Remember structure since that's the easiest
  1060.      *        way of keeping track of all these small memory allocations, and
  1061.      *        freeing them later on.
  1062.      */
  1063.     for (i = 0; i < taskCount && !error; i++)
  1064.         {
  1065.         if ((taskNode = AllocRemember(memoryKey, sizeof(struct ListType), MEMF_ANY))
  1066.             && (taskNode -> mainNode.ln_Name = AllocRemember(memoryKey, taskLength + 1, MEMF_ANY)))
  1067.             {
  1068.             /*
  1069.              *        Here we make a copy of the original pointer to the task
  1070.              *        (so we can later kill it, change its priority, etc.), copy
  1071.              *        its priority into the Node structure, fill in the neatly-
  1072.              *        joined name and priority using another function, and finally
  1073.              *        update its pointer in taskArray to point to the Node
  1074.              *        structure, since that's what we'll be using in future.
  1075.              */
  1076.             taskNode -> mainTask = taskArray[i];
  1077.             taskNode -> mainNode.ln_Pri = taskArray[i] -> tc_Node.ln_Pri;
  1078.             CreateString(taskArray[i], taskNode -> mainNode.ln_Name);
  1079.             taskArray[i] = (struct Task *)taskNode;
  1080.             }
  1081.         else
  1082.             {
  1083.             /*
  1084.              *        We ran out of memory trying to allocate memory for a single
  1085.              *        Node structure. Panic!!
  1086.              */
  1087.             error = TASK_ERROR;
  1088.             FreeRemember(memoryKey, TRUE);
  1089.             }
  1090.         }
  1091.  
  1092.     /*
  1093.      *        We need nothing more from Exec's goldmine of information, so
  1094.      *        allow things to continue normally and hope that the machine was
  1095.      *        not locked up for a noticeable period of time! After that, we only
  1096.      *        continue if the second pass went okay.
  1097.      */    
  1098.     Permit();
  1099.  
  1100.     if (!error)
  1101.         {
  1102.         /*
  1103.          *        The third pass involves creating a List structure, suitable for
  1104.          *        adding to the ListView. Into that structure we sort all the Node
  1105.          *         structures created above. The first thing to do is get a clean
  1106.          *        List...
  1107.          */
  1108.         taskList.lh_Head        = (struct Node *) &(taskList.lh_Tail);
  1109.         taskList.lh_Tail        = NULL;
  1110.         taskList.lh_TailPred    = (struct Node *) &(taskList.lh_Head);
  1111.  
  1112.         /*
  1113.          *        Now we go through all the entries in taskArray, adding them to
  1114.          *        this List structure in alphabetical order. For each entry, we go
  1115.          *        around in a loop as long as there are more tasks to compare
  1116.          *        against in the list and the current task in the list is
  1117.          *        alphabetically earlier than the one we're working on now. We use
  1118.          *        a special version of strcmp() which is case-insensitive and
  1119.          *        skips over leading brackets. Unfortunately, it doesn't sort CLI
  1120.          *        tasks properly (10 will come before 2) - sorry!
  1121.          */
  1122.         for (i = 0; i < taskCount; i++)
  1123.             {
  1124.             for (execNode = taskList.lh_Head;
  1125.                  (execNode -> ln_Succ) &&
  1126.                     CompareTasks(execNode -> ln_Name, ((struct ListType *)taskArray[i]) -> mainNode.ln_Name) < 0;
  1127.                  execNode = execNode -> ln_Succ)
  1128.                 ;  /* empty body */
  1129.  
  1130.             /*
  1131.              *        Once the loop has ended, we have just passed the position
  1132.              *        where we want to insert our task.
  1133.              */
  1134.             Insert(&taskList, (struct Node *)(taskArray[i]), execNode -> ln_Pred);
  1135.             }
  1136.  
  1137.         /*
  1138.          *        If there was a task already selected, we want to find it in the
  1139.          *        new ListView, and reselect it. Recall that current points to the
  1140.          *        selected ListView entry, currentTask points to the task
  1141.          *        structure, and pos is the ordinal number of the selection. If no
  1142.          *        task has been selected, current is NULL, pos is -1, and
  1143.          *        currentTask is undefined.
  1144.          */
  1145.         if (current)
  1146.             for (execNode = taskList.lh_Head, current = NULL, pos = 0;
  1147.                  execNode -> ln_Succ;
  1148.                  execNode = execNode -> ln_Succ, pos++)
  1149.                 if (((struct ListType *)execNode) -> mainTask == currentTask)
  1150.                     {
  1151.                     current = execNode;
  1152.                     break;
  1153.                     }
  1154.  
  1155.         /*
  1156.          *        If current is still NULL down here, it means there was
  1157.          *        either no task selected before, or else the one selected could
  1158.          *        no longer be found. Otherwise, we still have a selected task,
  1159.          *        and need to update the slider gadget and menu items, just in
  1160.          *        case its priority has changed in the meantime.
  1161.          */
  1162.         if (current)
  1163.             OnTask();
  1164.         else
  1165.             OffTask();
  1166.  
  1167.         /*
  1168.          *        We need to move the currently-selected entry into view. We
  1169.          *        use the GTLV_Top tag for starters, since we'll always have a
  1170.          *        reasonable value for that.
  1171.          */
  1172.         GT_SetGadgetAttrs(listGad, mainWindow, NULL,
  1173.                             GTLV_Labels,        &taskList,
  1174.                             GTLV_Selected,        pos,
  1175.                             GTLV_Top,            top,
  1176.                             TAG_END);
  1177.  
  1178.         /*
  1179.          *        If there is a task selected, we also use the GTLV_MakeVisible
  1180.          *        tag (which only works under 3.x) to try even harder to restore
  1181.          *        the ListView to its original position. We need to do this in a
  1182.          *        separate step to the first call above, since the second tag
  1183.          *        completely overrides the first if they are used together.
  1184.          */
  1185.         if (current)
  1186.             GT_SetGadgetAttrs(listGad, mainWindow, NULL,
  1187.                                 GTLV_MakeVisible,    pos,
  1188.                                 TAG_END);
  1189.  
  1190.         /*
  1191.           *        Now we invalidate "top", so we'll know whether or not to fill it
  1192.          *        in at the start of this function the next time round.
  1193.          */
  1194.         top = -1;
  1195.  
  1196.         /*
  1197.          *        Here we update PriMan's title bar to reflect the number of tasks
  1198.          *        listed.
  1199.          */
  1200.         sprintf(title, "PriMan - %ld tasks", taskCount);
  1201.         SetWindowTitles(mainWindow, title, (UBYTE *)~0);
  1202.  
  1203.         /*    
  1204.          *        If necessary, we need to disable the slider, break and kill
  1205.          *        gadgets, and associated menu items.
  1206.          */
  1207.         GT_SetGadgetAttrs(sliderGad, mainWindow, NULL,
  1208.                             GA_Disabled,    pos == -1,
  1209.                             TAG_END);
  1210.         GT_SetGadgetAttrs(breakGad, mainWindow, NULL,
  1211.                             GA_Disabled,    pos == -1,
  1212.                             TAG_END);
  1213.         GT_SetGadgetAttrs(killGad, mainWindow, NULL,
  1214.                             GA_Disabled,    pos == -1,
  1215.                             TAG_END);
  1216.     
  1217.         /*
  1218.          *        We allow the situation where there were more tasks than our
  1219.          *        hard-coded maximum. Since this isn't the user's fault (he
  1220.          *        didn't run out of memory; we're just being lazy!), it's probably
  1221.          *        polite to let him know that the list will be incomplete.
  1222.          */
  1223.         if (taskCount == MaxTasks)
  1224.             SimpleRequest(&task, NULL);
  1225.         }
  1226.     }
  1227.  
  1228.  
  1229. /*
  1230.  *        Given a Task structure, extract the name and priority, then join them
  1231.  *        together to fit exactly in taskLength characters.
  1232.  *
  1233.  *        It is possible that the task is actually a CLI process, in which case
  1234.  *        we need to add "CLI #x" before it.
  1235.  *
  1236.  *        If the task is frozen, the portion of its name that is visible must be
  1237.  *        bracketed. Our task (excuse the pun) is made easier since we know that
  1238.  *        there is always enough space in the ListView for at least one letter of
  1239.  *        the task name.
  1240.  *
  1241.  *        There must be at least length+1 bytes free in dest, because of the \0
  1242.  *        character.
  1243.  */
  1244. void CreateString(struct Task *task, char *dest)
  1245.     {
  1246.     int        taskChars,        /* max number of chars of the task name we can use    */
  1247.             length,            /* length of task name                                */
  1248.             i, j;            /* index into task name                                */
  1249.  
  1250.     LONG    cli;            /* task's CLI process number                        */
  1251.  
  1252.     BOOL    frozen;            /* task is frozen                                    */
  1253.  
  1254.     char    digits[5],        /* enough to hold "-128" and \0 char                */
  1255.             cliText[11],    /* enough to hold "CLI #999: " and \0 char            */
  1256.             *name;            /* pointer to task name                                */
  1257.  
  1258.     APTR    bstr;            /* BCPL structs are used if task is a CLI process    */
  1259.  
  1260.     /*
  1261.      *        The first thing we'll do is convert the task priority into a
  1262.      *        sequence of characters. This will allow us to see exactly how many
  1263.      *        letters in the task name we can fit. Recall that sprintf() returns
  1264.      *        the number of characters it processes. We also allow room for a
  1265.      *        space character between the name and priority.
  1266.      */
  1267.     sprintf(digits, "%ld", task -> tc_Node.ln_Pri);
  1268.     taskChars = taskLength - strlen(digits) - 1;
  1269.  
  1270.     /*
  1271.      *        If the task was frozen, we want to bracket its name. Here we insert
  1272.      *        the opening bracket, set a flag to remind us to close it again, and
  1273.      *        then update taskChars if necessary to allow for the brackets.
  1274.      */
  1275.     if (Frozen(task))
  1276.         {
  1277.         (*dest++) = '(';
  1278.         frozen = TRUE;
  1279.         taskChars -= 2;
  1280.         }
  1281.     else
  1282.         frozen = FALSE;
  1283.  
  1284.     /*
  1285.      *        Now we obtain a pointer to the task name and get its length, in
  1286.      *        preparation for copying it. If the task is a process and has a CLI
  1287.      *        attached, we fill in the cliText variable and use that first when
  1288.      *        copying.
  1289.      */
  1290.     if (((task -> tc_Node.ln_Type) == NT_PROCESS) &&
  1291.         (cli = ((struct Process *)task) -> pr_TaskNum))
  1292.         {
  1293.         sprintf(cliText, "CLI #%ld: ", cli);
  1294.         /*
  1295.          *        This next bit is hairy, thanks to BCPL. The CLI name is
  1296.          *        actually a BCPL string, i.e. it has the length in its
  1297.          *        first byte, and doesn't contain a string terminator. So
  1298.          *        we need to walk through some BCPL structures to find the
  1299.          *        CLI name, and then extract its length from that.
  1300.          */
  1301.         bstr    = BADDR(((struct CommandLineInterface *)
  1302.                     (BADDR(((struct Process *)task) -> pr_CLI))) -> cli_CommandName);
  1303.         name    = (char *)(bstr) + 1;
  1304.         length    = *((BYTE *)bstr);
  1305.         }
  1306.     else
  1307.         {
  1308.         /*
  1309.          *        Tasks without a CLI structure have it much easier! We just need
  1310.          *        to remember to add a \0 character to the start of cliText so we
  1311.          *        don't try copying it later.
  1312.          */
  1313.         name = task -> tc_Node.ln_Name;
  1314.         length = strlen(name);
  1315.         cliText[0] = '\0';
  1316.         }
  1317.  
  1318.     /*
  1319.      *        Copying the task name involves a character-by-character copy of
  1320.      *        both the "CLI #x" text if applicable, and the task name. The loop
  1321.      *        ends when:
  1322.      *
  1323.      *        - We run out of space in dest (taskChars reaches 0), or;
  1324.      *
  1325.      *        - We have finished copying both the CLI text (character at current
  1326.      *          position is \0), and the task name (length reaches 0).
  1327.      */
  1328.     i = j = 0;
  1329.     while (taskChars-- && (cliText[i] || length))
  1330.         if (cliText[i])
  1331.             *(dest++) = cliText[i++];
  1332.         else
  1333.             {
  1334.             *(dest++) = name[j++];
  1335.             length--;  /* only decrement this if we've moved onto the task name! */
  1336.             }
  1337.  
  1338.     /*
  1339.      *        We mustn't forget to close our brackets if the task was frozen.
  1340.      */
  1341.     if (frozen)
  1342.         *(dest++) = ')';
  1343.  
  1344.     /*
  1345.      *        At this point, we might have finished copying the task name, but we
  1346.      *        still need extra spacing characters. We'll add 2 to taskChars - one
  1347.      *        extra obligatory space (accounted for at the start of the function),
  1348.      *        and one to counter the effect of it reaching -1 at the end of the
  1349.      *        previous loop (instead of 0).
  1350.      */
  1351.     taskChars += 2;
  1352.     while (taskChars--)
  1353.         *(dest++) = ' ';
  1354.  
  1355.     /*
  1356.      *        Adding the priority is easy, since we know we're at the exact right
  1357.      *        position for it now. A \0 char will get added on automatically here.
  1358.      */
  1359.     strcpy(dest, digits);
  1360.     }
  1361.